<template>
    <div :class="[
        'handler',
        { dragging },
        direction,
        { 'show-handler': showHandler },
    ]" ref="handlerRef" />
</template>
<script lang="ts" setup>
import { computed, inject, ref, watchEffect } from 'vue';
import { useMousePressed, useMouse } from '@vueuse/core';
import { IResizerProps } from './types';

let deltaCache = 0;
const props = defineProps({
    index: {
        type: Number,
        required: true,
    },
});
const isVertical = computed(() => direction === 'column');
const {
    sizes,
    containerRef,
    direction,
    showHandler,
    handlerColor,
    handlerSize,
} = inject<IResizerProps>('vue_resizer');
const handlerRef = ref<HTMLElement>();
function onDrag(event: MouseEvent) {
    if (!dragging.value) return;
    const p = isVertical.value ? event.clientY : event.clientX;
    const { top, left } = handlerRef.value.getBoundingClientRect();
    const position = isVertical.value ? top : left;
    const delta = p - position;
    const rate = calcDelta(delta);

    if (!delta) return;
    if (delta * deltaCache < 0) {
        syncSizes();
    }
    deltaCache = delta;
    sizes.value = sizes.value.map((number, index) => {
        if (index === props.index) {
            return Number((number + rate).toFixed(2));
        } else if (index === props.index + 1) {
            return Number((number - rate).toFixed(2));
        } else {
            return number;
        }
    });
}
function calcDelta(delta: number) {
    const boundSize = isVertical.value
        ? containerRef.value.clientHeight
        : containerRef.value.clientWidth;
    const totalRate = sizes.value.reduce((a, b) => a + b, 0);
    if (boundSize && totalRate) {
        const px2ratio = totalRate / boundSize;
        return Number((delta * px2ratio).toFixed(2));
    } else {
        return 0;
    }
}
function syncSizes() {
    const container = containerRef.value;
    const children = container.children;
    sizes.value = Array.from(children).map((child) => {
        const rect = child.getBoundingClientRect();
        return isVertical.value ? rect.height : rect.width;
    });
}

const { x, y } = useMouse({
    target: containerRef,
});
const { pressed: dragging } = useMousePressed({
    target: handlerRef,
});
watchEffect(() => {
    if (dragging.value) {
        onDrag({ clientX: x.value, clientY: y.value } as MouseEvent);
    }
});
</script>
<style scoped>
.handler {
    position: absolute;
    background: v-bind(handlerColor);
    opacity: 0;

    &.show-handler {
        opacity: .1;
    }

    &.column {
        cursor: row-resize;
        bottom: 0;
        left: 0;
        right: 0;
        height: v-bind(handlerSize + 'px');
        width: 100%;
    }

    &.row {
        cursor: col-resize;
        top: 0;
        bottom: 0;
        right: 0;
        width: v-bind(handlerSize + 'px');
        height: 100%;
    }

    &:hover,
    &.dragging {
        opacity: 1;
        user-select: none;
    }
}
</style>
